package com.orangecy.rulesubscribe.callback;

import com.google.common.collect.Lists;
import com.orangecy.common.exception.OrangeCyException;
import com.orangecy.rulesubscribe.RuleEngineResult;
import com.orangecy.rulesubscribe.actuator.ActuatorStrategy;
import com.orangecy.rulesubscribe.actuator.ActuatorStrategyContainer;
import com.orangecy.rulesubscribe.config.RuleDefinePoolConfig;
import com.orangecy.rulesubscribe.config.SubscriberDefinePoolConfig;
import com.orangecy.rulesubscribe.context.RuleContext;
import com.orangecy.rulesubscribe.data.RunDataKeyEnum;
import com.orangecy.rulesubscribe.define.RuleActuatorDTO;
import com.orangecy.rulesubscribe.define.RuleDefineDTO;
import com.orangecy.rulesubscribe.define.SubscribeRuleDTO;
import com.orangecy.rulesubscribe.define.SubscriberDefineDTO;
import com.orangecy.rulesubscribe.enums.ActuatorTypeEnum;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Author : orangeCy.奕
 * @Product : 猎豹养成产品源码
 * @Time : 2021/10/22
 * @Desc :
 */
public class GeneralActionCallBack extends AbstractActionCallBack<RuleEngineResult, RuleContext> {

    @Resource
    private ActuatorStrategyContainer actuatorStrategyContainer;

    @Override
    public void valParamContext(RuleContext context) {
        if(StringUtils.isEmpty(context.getSubscriberCode())){
            throw new OrangeCyException("订阅者code参数不能为空");
        }
        //OrangeCyLog orangeCyLog = context.getOrangeCyLog();
        //orangeCyLog.pKey("")
        //移到前面比较好
    }

    @Override
    public void routerSubscriber(RuleContext context) {
        SubscriberDefineDTO subscriberDefineDTO = SubscriberDefinePoolConfig.findSubscriberByCode(context.getSubscriberCode());
        //记录至上下文共享 注意这里没有 thread safe 问题
        context.getFeatureDataMap().put(RunDataKeyEnum.SUBSCRIBER_DEFINE_ENTITY, subscriberDefineDTO);
    }

    @Override
    public void mappingSubscribeRules(RuleContext context) {
        //从上节点获取订阅者
        SubscriberDefineDTO subscriberDefineDTO = context.getFeatureDataMap().get(RunDataKeyEnum.SUBSCRIBER_DEFINE_ENTITY, SubscriberDefineDTO.class);
        //根据订阅者路由mapping映射所有的规则
        List<SubscribeRuleDTO> subscribeRuleDTOS =
                SubscriberDefinePoolConfig.mappingSubscribeRules(subscriberDefineDTO);
        //记录关系至上下文共享 注意这里没有 thread safe 问题
        context.getFeatureDataMap().putList(RunDataKeyEnum.SUBSCRIBE_RULE_DTO_LIST, subscribeRuleDTOS,SubscribeRuleDTO.class);
    }

    @Override
    public void ruleProcess(RuleContext context) {
        //取出关系规则集合
        List<SubscribeRuleDTO> subscribeRuleDTOList = context.getFeatureDataMap().getList(RunDataKeyEnum.SUBSCRIBE_RULE_DTO_LIST, SubscribeRuleDTO.class);
        if(CollectionUtils.isEmpty(subscribeRuleDTOList)) {
            //record log
            return;
        }
        List<String> subscribeRuleCodes = subscribeRuleDTOList.stream().map(SubscribeRuleDTO::getRuleCode).collect(Collectors.toList());
        //查询规则
        List<RuleDefineDTO> subscribeRules = RuleDefinePoolConfig.findRulesByCodes(subscribeRuleCodes);
        List<RuleDefineDTO> executeRules = subscribeRules.stream().filter(e -> Objects.nonNull(e.getActuatorType()))
                .filter(e -> Objects.nonNull(e.getActuatorProcessor()))
                .collect(Collectors.toList());
        if(CollectionUtils.isEmpty(executeRules)) {
            //record log
            return;
        }
        List<String> executeCodes = executeRules.stream().map(RuleDefineDTO::getRuleCode).collect(Collectors.toList());
        //record log for executeCodes
        //todo context.getOrangeCyLog()

        //process
        List<RuleActuatorDTO> ruleActuatorDTOS = ruleProcessByActuator(context, executeRules);

        //记录执行结果至上下文map
        context.getFeatureDataMap().putList(RunDataKeyEnum.RULE_COMPONENT_ACTUATOR_LIST,ruleActuatorDTOS,RuleActuatorDTO.class);
    }

    @Override
    public RuleEngineResult assemble(RuleContext context) {
        RuleEngineResult ruleEngineResult = new RuleEngineResult();
        ruleEngineResult.setSubscriberCode(context.getSubscriberCode());
        String subscriberName = context.getFeatureDataMap().get(RunDataKeyEnum.SUBSCRIBER_DEFINE_ENTITY, SubscriberDefineDTO.class).getSubscriberName();
        ruleEngineResult.setSubscriberName(subscriberName);
        List<RuleActuatorDTO> ruleActuatorDTOList = context.getFeatureDataMap().getList(RunDataKeyEnum.RULE_COMPONENT_ACTUATOR_LIST, RuleActuatorDTO.class);
        ruleEngineResult.setCrossOverRules(ruleActuatorDTOList);
        boolean ruleIsAllPass = ruleEngineResult.getCrossOverRules().stream()
                .map(RuleActuatorDTO::getActuatorRes).filter(Objects::nonNull).allMatch(Boolean.TRUE::equals);

        ruleEngineResult.setRuleIsAllPass(ruleIsAllPass);
        if(!ruleIsAllPass) {
            ruleEngineResult.setRuleFailMsg(ruleEngineResult.getCrossOverRules().stream()
                     .filter(e -> !Boolean.TRUE.equals(e.getActuatorRes()))
                     .map(RuleActuatorDTO::getActuatorMsg).findFirst().orElse(null) );
        }
        return ruleEngineResult;
    }


    private List<RuleActuatorDTO> ruleProcessByActuator(RuleContext context,List<RuleDefineDTO> executeRules) {
        List<RuleActuatorDTO> overCrossRuleActuator = Lists.newArrayList();
        for(RuleDefineDTO rule : executeRules) {
            ActuatorTypeEnum actuatorTypeEnum = ActuatorTypeEnum.resolve(rule.getActuatorType());
            if(Objects.isNull(actuatorTypeEnum)) {
                throw new OrangeCyException("actuatorTypeEnum not parse");
            }
            //策略模式 获取执行
            ActuatorStrategy actuatorStrategy = actuatorStrategyContainer.getStrategy(String.valueOf(actuatorTypeEnum.getType()));
            if(Objects.isNull(actuatorStrategy)) {
                throw new OrangeCyException("actuatorStrategy is null");
            }
            RuleActuatorDTO actuator = actuatorStrategy.actuator(context, rule);
            if(Objects.isNull(actuator)) {
                throw new OrangeCyException("actuator is null");
            }
            overCrossRuleActuator.add(actuator);
            if(!BooleanUtils.isTrue(actuator.getActuatorRes())) {
                break;
            }
        }
        return overCrossRuleActuator;
    }
}